﻿using System;
using System.Collections.Generic;
using System.Linq;
using Xunit;

namespace UniSender.Test
{
    public class TestClientEvents
    {
        [Fact]
        public void TestRequestStartEvent()
        {
            var client = new ClientStub("tt", "test")
            {
                Deserializer = new DeserializerStub() { StubDeserialize = s => new Native.Response<object>() }
            };
            UniSenderEventArgs eventArgs = null;
            client.OnRequestStart += e => {
                eventArgs = e;
            };
            client.TryCall<object>("action");
            Assert.NotNull(eventArgs);
            Assert.Equal("tt", eventArgs.Lang);
            Assert.Equal("action", eventArgs.Method);
            Assert.Null(eventArgs.Args);
        }

        [Fact]
        public void TestRequestSuccessEvent()
        {
            Native.IResponse response = new Native.Response<object>();
            var client = new ClientStub("tt", "test")
            {
                Deserializer = new DeserializerStub() { StubDeserialize = s => response }
            };
            UniSenderRequestSuccessEventArgs eventArgs = null;
            client.OnRequestSuccess += e =>
            {
                eventArgs = e;
            };
            client.TryCall<object>("action");
            Assert.NotNull(eventArgs);
            Assert.Equal("tt", eventArgs.Lang);
            Assert.Equal("action", eventArgs.Method);
            Assert.Null(eventArgs.Args);
            Assert.Equal("{}", eventArgs.ResponseText);
            Assert.Same(response, eventArgs.Response);
        }

        [Fact]
        public void TestWarningEvent()
        {
            var client = new ClientStub("tt", "test")
            {
                Deserializer = new DeserializerStub()
                {
                    StubDeserialize = s => new Native.Response<object>()
                    {
                        result = new object(),
                        warnings = new Native.Warning[] { new Native.Warning() { warning = "Warning 1" }, new Native.Warning() { warning = "Warning 2" } }
                    }
                }
            };
            UniSenderWarningEventArgs eventArgs = null;
            client.OnWarning += e =>
            {
                eventArgs = e;
            };
            client.TryCall<object>("action");
            Assert.NotNull(eventArgs);
            Assert.Equal("tt", eventArgs.Lang);
            Assert.Equal("action", eventArgs.Method);
            Assert.Null(eventArgs.Args);
            Assert.Equal(2, eventArgs.Warnings.Length);
            Assert.Equal("Warning 2", eventArgs.Warnings[1]);
        }

        [Fact]
        public void TestErrorEvent()
        {
            var client = new ClientStub("tt", "test")
            {
                Deserializer = new DeserializerStub()
                {
                    StubDeserialize = s => new Native.Response<object>()
                    {
                        code = "unknown_method",
                        error = "Unknown method"
                    }
                }
            };
            UniSenderErrorEventArgs eventArgs = null;
            client.OnError += e =>
            {
                eventArgs = e;
            };
            client.TryCall<object>("action");
            Assert.NotNull(eventArgs);
            Assert.Equal("tt", eventArgs.Lang);
            Assert.Equal("action", eventArgs.Method);
            Assert.Null(eventArgs.Args);
            Assert.Equal("unknown_method", eventArgs.Code);
            Assert.Equal("Unknown method", eventArgs.Error);
        }

        [Fact]
        public void TestExceptionEvent()
        {
            var exc = new Exception("Test");
            UniSenderExceptionEventArgs eventArgs;
            var client = new ClientStub("tt", "test");
            client.OnException += e =>
            {
                eventArgs = e;
            };

            eventArgs = null;
            client.StubRequest = (m, a) => { throw exc; };
            client.TryCall<object>("action");
            Assert.NotNull(eventArgs);
            Assert.Equal("tt", eventArgs.Lang);
            Assert.Equal("action", eventArgs.Method);
            Assert.Null(eventArgs.Args);
            Assert.NotNull(eventArgs.Exception);
            Assert.IsType<UniSenderTransportException>(eventArgs.Exception);
            Assert.Same(exc, eventArgs.Exception.InnerException);

            eventArgs = null;
            client.StubRequest = null;
            client.Deserializer = new DeserializerStub() { StubDeserialize = s => { throw exc; } };
            client.TryCall<object>("action");
            Assert.NotNull(eventArgs);
            Assert.Equal("tt", eventArgs.Lang);
            Assert.Equal("action", eventArgs.Method);
            Assert.Null(eventArgs.Args);
            Assert.NotNull(eventArgs.Exception);
            Assert.IsType<UniSenderParseException>(eventArgs.Exception);
            Assert.Same(exc, eventArgs.Exception.InnerException);

            eventArgs = null;
            client.StubRequest = null;
            client.Deserializer = new DeserializerStub()
            {
                StubDeserialize = s => new Native.Response<object>()
                {
                    error = "Test error",
                    code = "test_error"
                }
            };
            client.TryCall<object>("action");
            Assert.Null(eventArgs);

            eventArgs = null;
            client.StubRequest = (m, a) => { throw exc; }; ;
            client.Deserializer = new DeserializerStub() { StubDeserialize = s => new Native.Response<object>() };
            Assert.Throws<UniSenderTransportException>(() => {
                client.Call<object>("action");
            });
            Assert.Null(eventArgs);
        }

        [Fact]
        public void TestArgsInEvent()
        {
            int calls = 0;
            var exc = new Exception("Test");
            var args = new Dictionary<string, object>()
            {
                { "arg1", 123 },
                { "arg2", "argTest" }
            };
            Action<UniSenderEventArgs> handler = e =>
            {
                calls++;
                Assert.NotNull(e.Args);
                Assert.Equal(2, e.Args.Count);
                Assert.Equal(new string[] { "arg1", "arg2" }, e.Args.Keys.ToArray());
                Assert.Equal(new object[] { 123, "argTest" }, e.Args.Values.ToArray());
            };
            var client = new ClientStub("tt", "test")
            {
                Deserializer = new DeserializerStub() { StubDeserialize = s => new Native.Response<object>() }
            };
            client.OnRequestStart += handler;
            client.OnRequestSuccess += handler;
            client.OnWarning += handler;
            client.OnError += handler;
            client.OnException += handler;

            client.TryCall<object>("action", args);
            client.OnRequestStart -= handler;
            client.OnRequestSuccess -= handler;

            client.Deserializer = new DeserializerStub()
            {
                StubDeserialize = s => new Native.Response<object>()
                {
                    warnings = new Native.Warning[] { new Native.Warning() { warning = "Test Warning" } }
                }
            };
            client.TryCall<object>("action", args);

            client.Deserializer = new DeserializerStub()
            {
                StubDeserialize = s => new Native.Response<object>()
                {
                    error = "Test error",
                    code = "test_error"
                }
            };
            client.TryCall<object>("action", args);

            client.StubRequest = (m, a) => { throw exc; };
            client.TryCall<object>("action", args);
            Assert.Equal(5, calls);
        }
    }
}
